/**
 * 
 */
package mblog.core.persist.dao.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;

import com.faxsun.core.persist.entity.TagPO;
import com.faxsun.core.pojos.enums.TagType;

import mblog.core.persist.dao.TagDao;
import mblog.core.pojos.Tag;
import mtons.modules.persist.impl.DaoImpl;
import mtons.modules.pojos.Page;

/**
 * @author langhsu
 * @author songdragon
 * 
 */
public class TagDaoImpl extends DaoImpl<TagPO> implements TagDao {

	private static final Logger LOG = LoggerFactory.getLogger(TagDaoImpl.class);

	private static final long serialVersionUID = 3787316111507159374L;

	public TagDaoImpl() {
		super(TagPO.class);
	}

	@Override
	public TagPO getByName(String name) {
		return findUniqueBy("name", name);
	}

	@Override
	public List<TagPO> tops(int maxResults) {
		TopQuery<TagPO> q = topQuery(maxResults);
		q.desc("featured");
		q.desc("hots");
		q.add(Restrictions.eq("isDeleted", 0));
		return q.list();
	}

	@Override
	public List<TagPO> findByIds(Collection<Long> ids) {
		return find(Restrictions.in("id", ids));
	}

	/**
	 * 查找推荐产品，按featured和productId降序排列
	 */
	@Override
	public List<TagPO> findForRecommendProductsTag(int maxResults) {
		TopQuery<TagPO> q = topQuery(maxResults);
		q.add(Restrictions.eq("type", TagType.PRODUCT.getValue()));
		// q.add(Restrictions.ne("featured", 0));
		// FIXME:use delete type enum isntead of 0
		q.add(Restrictions.eq("isDeleted", 0));
		q.desc("featured");
		return q.list();
	}

	@Override
	public List<TagPO> list(Page page, TagType type) {
		Criteria criteria = this.createCriteria();
		//热度降序
		criteria.addOrder(Order.desc("hots"));
		//文章数降序
		criteria.addOrder(Order.desc("posts"));
		//视频降序
		criteria.addOrder(Order.desc("videoUrl"));
		//推荐度降序
		criteria.addOrder(Order.desc("featured"));
		criteria.addOrder(Order.asc("id"));
		// FIXME: isDeleted value is rewrite in Enum
		criteria.add(Restrictions.eq("isDeleted", 0));

		Criteria count = this.createCriteria().add(
				Restrictions.eq("isDeleted", 0));

		if (type != null && !type.equals(TagType.INVALID)) {
			criteria.add(Restrictions.eq("type", type.getValue()));
			count.add(Restrictions.eq("type", type.getValue()));
		}

		count.setProjection(Projections.count("id"));
		Long total = (Long) count.list().get(0);
		page.setTotalCount(total.intValue());

		criteria.setFirstResult(page.getFirstResult());
		if (page.getMaxResults() > 0) {
			criteria.setMaxResults(page.getMaxResults());
		}
		@SuppressWarnings("unchecked")
		List<TagPO> ret = criteria.list();
		return ret;
	}

	@SuppressWarnings("unchecked")
	public List<TagPO> queryTag(String tagName, Page page) {

		Criteria criteria = this.createCriteria();
		criteria.add(Restrictions.like("name", tagName, MatchMode.ANYWHERE));
		criteria.addOrder(Order.asc("id"));
		criteria.setMaxResults(page.getMaxResults());
		criteria.add(Restrictions.eq("isDeleted", 0));
		List<TagPO> ret = criteria.list();
		return ret;
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<TagPO> listTagByCategory(Page page, List<TagPO> categoryTags) {
		// TODO Auto-generated method stub
		List<TagPO> ret = new ArrayList<TagPO>();
		// if (categoryTags == null || categoryTags.size() == 0) {
		// return ret;
		// }

		String hql = "select product from TagPO product "
				+ "join product.tagsCategorySet category "
				+ "where category.id in (:categories) and product.type=:type "
				+ "and product.isDeleted=0 " + "group by product "
				+ "having count(category)=:categories_count "
				+ "order by product.id desc";
		List<Long> categoriesId = new ArrayList<Long>();
		for (TagPO tagPO : categoryTags) {
			categoriesId.add(tagPO.getId());
		}
		if (page.isCount()) {
			String countHql = "select count(product) from TagPO product "
					+ "join product.tagsCategorySet category "
					+ "where category.id in (:categories) and product.type=:type "
					+ "and product.isDeleted=0 " + "group by product "
					+ "having count(category)=:categories_count "
					+ "order by product.id desc";
			Query query = this.createQuery(countHql);

			query.setParameterList("categories", categoriesId);
			query.setInteger("categories_count", categoriesId.size());
			query.setInteger("type", TagType.PRODUCT.getValue());
			Integer count = (Integer) query.list().size();
			page.setTotalCount(count);
		}

		/*
		 * Criteria criteria=this.createCriteria();
		 * criteria.createAlias("tagsCategorySet", "categorySet"); List<Long>
		 * categoriesId=new ArrayList<Long>(); for(TagPO tagPO:categoryTags){
		 * categoriesId.add(tagPO.getId()); }
		 * criteria.add(Restrictions.in("categorySet.id", categoriesId));
		 * //FIXME:排序标准 criteria.addOrder(Order.desc("id"));
		 * criteria.setFirstResult(page.getFirstResult());
		 * criteria.setMaxResults(page.getMaxResults()); ret=criteria.list();
		 */

		Query query = this.createQuery(hql);
		query.setParameterList("categories", categoriesId);
		query.setInteger("categories_count", categoriesId.size());
		query.setInteger("type", TagType.PRODUCT.getValue());
		query.setFirstResult(page.getFirstResult());
		if (page.getMaxResults() > 0)
			query.setMaxResults(page.getMaxResults());
		ret = query.list();
		return ret;
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<TagPO> listNavTags() {
		Criteria criteria = this.createCriteria();
		criteria.add(Restrictions.eq("type", TagType.CATEGORY.getValue()));
		criteria.add(Restrictions.eq("isNav", Boolean.TRUE));
		criteria.add(Restrictions.eq("isDeleted", 0));
		criteria.addOrder(Order.desc("id"));
		return criteria.list();
	}

	@SuppressWarnings("unchecked")
	@Override
	@Transactional(readOnly = true)
	public List<TagPO> findRecommendProductsForCategory(Tag categoryTag,
			int maxResult) {

		if (categoryTag == null || categoryTag.getId() < 1L) {
			return new ArrayList<TagPO>();
		}

		String hql = "select product from TagPO product "
				+ "join product.tagsCategorySet category "
				+ "where category.id = (:categorieId) and product.type=:type "
				+ "and product.isDeleted=0 "
				+ "order by product.featured,product.id desc";
		Query query = this.createQuery(hql);
		query.setLong("categorieId", categoryTag.getId());
		query.setInteger("type", TagType.PRODUCT.getValue());
		query.setMaxResults(maxResult);
		/*
		 * TopQuery<TagPO> q = topQuery(maxResult);
		 * q.add(Restrictions.eq("type", TagType.PRODUCT.getValue()));
		 * q.alias("tagsCategorySet", "categorySet");
		 * q.add(Restrictions.eq("categorySet.name", categoryTag.getName()));
		 * q.add(Restrictions.eq("isDeleted", 0)); q.desc("featured"); return
		 * q.list();
		 */
		return query.list();
	}

	@Override
	public TagPO findByProductRefId(Long id) {
		Criteria criteria = this.createCriteria();
		criteria.add(Restrictions.eq("productRef.id", id));
		@SuppressWarnings("unchecked")
		List<TagPO> ret = (List<TagPO>) criteria.list();
		if (ret.size() > 0){
			if(ret.size()>1){
				LOG.warn("Multiple tag with same productRefPO. ProductRefPO.id=" +id);
			}
			return ret.get(0);
		}
		return null;
	}
}
